home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / djgpp / demos / puzzle / main.c < prev    next >
Text File  |  1994-08-12  |  30KB  |  1,219 lines

  1. /* $XConsortium: main.c,v 1.15 91/02/18 18:04:16 converse Exp $ */
  2.  
  3. /* Puzzle - (C) Copyright 1987, 1988 Don Bennett.
  4.  *
  5.  * Permission to use, copy, modify, and distribute this software and its
  6.  * documentation for any purpose and without fee is hereby granted,
  7.  * provided that the above copyright notice appear in all copies and that
  8.  * both that copyright notice and this permission notice appear in
  9.  * supporting documentation.
  10.  */
  11.  
  12. #define DEBUG
  13. #define USE_PICTURE
  14. #define SERVER_BUG
  15.  
  16. /**  Puzzle
  17.  **
  18.  ** Don Bennett, HP Labs 
  19.  ** 
  20.  ** this is the interface code for the puzzle program.
  21.  **/
  22.  
  23. #include <stdio.h>
  24. #include <X11/Xos.h>
  25. #include <X11/Xatom.h>
  26. #include <X11/Xlib.h>
  27. #include <X11/Xutil.h>
  28.  
  29. #include "ac.cursor"
  30. #include "ac_mask"
  31.  
  32. #define max(x,y)    ((x)>(y)?(x):(y))
  33. #define min(x,y)    ((x)>(y)?(y):(x))
  34. #define abs(x)        ((x)>0?(x):-(x))
  35.  
  36. #define PUZZLE_BORDER_WIDTH    2
  37.  
  38. #define TITLE_WINDOW_HEIGHT    25
  39. #define BOUNDARY_HEIGHT        3
  40.  
  41. #define BOX_WIDTH        10
  42. #define BOX_HEIGHT        10
  43.  
  44. #define MIN_TILE_HEIGHT        30
  45. #define MIN_TILE_WIDTH        30
  46.  
  47. #define MAX_STEPS        1000
  48. #define DEFAULT_SPEED        5
  49.  
  50. #define TITLE_TILES    0
  51. #define TITLE_TEXT    1
  52. #define TITLE_ANIMATED    2
  53.  
  54. int     BoxWidth  =    BOX_WIDTH;
  55. int    BoxHeight =    BOX_HEIGHT;
  56.  
  57. int    PuzzleSize = 4;
  58. int    PuzzleWidth=4, PuzzleHeight=4;
  59. char    defaultPuzzleSize[] = "4x4";
  60.  
  61. int    TileHeight, TileWidth;
  62. int    TextXStart;
  63. int     TitleWinHeight, BoundaryHeight, TileWinHeight;
  64.  
  65. int    FgPixel, BgPixel;
  66.  
  67. Display     *dpy;
  68. int        screen;
  69. GC        gc, rect_gc;
  70. Colormap    PuzzleColormap;
  71.  
  72. typedef struct {
  73.     Window    root;
  74.     int        x,y;
  75.     unsigned int    width, height;
  76.     unsigned int    border_width;
  77.     unsigned int    depth;
  78. } WindowGeom;
  79.  
  80. WindowGeom    PuzzleWinInfo;
  81.  
  82. Window         PuzzleRoot, TitleWindow=0, TileWindow,
  83.             ScrambleWindow, SolveWindow;
  84.  
  85. char        *ProgName;
  86.  
  87. char        *TitleFontName    = "8x13";
  88. char        *TileFontName     = "8x13bold";
  89.  
  90. XFontStruct    *TitleFontInfo,
  91.         *TileFontInfo;
  92.  
  93. extern int    OutputLogging;
  94. extern int    *position;
  95. extern int    space_x, space_y;
  96.  
  97. int    UsePicture = 0;
  98. int    UseDisplay = 0;
  99. int    CreateNewColormap = 0;
  100. char    *PictureFileName;
  101.  
  102. long    PictureWidth;
  103. long    PictureHeight;
  104. Pixmap    PicturePixmap;
  105.  
  106. int    TilesPerSecond;
  107. int    MoveSteps;
  108. int    VertStepSize[MAX_STEPS];
  109. int    HoriStepSize[MAX_STEPS];
  110.  
  111. #define LEFT    0
  112. #define RIGHT    1
  113. #define UP    2
  114. #define    DOWN    3
  115.  
  116. #define indx(x,y)    (((y)*PuzzleWidth) + (x))
  117. #define isdigit(x)    ((x)>= '0' && (x) <= '9')
  118.  
  119. #define ulx(x,y)    ((x)*TileWidth)
  120. #define llx(x,y)    ((x)*TileWidth)
  121. #define urx(x,y)    (((x)+1)*TileWidth - 1)
  122. #define lrx(x,y)    (((x)+1)*TileWidth - 1)
  123. #define uly(x,y)    ((y)*TileHeight)
  124. #define ury(x,y)    ((y)*TileHeight)
  125. #define lly(x,y)    (((y)+1)*TileHeight - 1)
  126. #define lry(x,y)    (((y)+1)*TileHeight - 1)
  127.  
  128. /*
  129.  * PuzzlePending - XPending entry point fo the other module.
  130.  */
  131.  
  132. PuzzlePending()
  133. {
  134.     return(XPending(dpy));
  135. }
  136.  
  137. /*
  138.  * SetupDisplay - eastablish the connection to the X server.
  139.  */
  140.  
  141. SetupDisplay(server)
  142. char *server;
  143. {
  144.     dpy = XOpenDisplay(server);
  145.     if (dpy == NULL) {
  146.     fprintf(stderr, "%s: unable to open display '%s'\n",
  147.         ProgName, XDisplayName (server));
  148.     exit(1);
  149.     } 
  150.     screen = DefaultScreen(dpy);
  151. #ifdef DEBUG
  152.     XSynchronize(dpy,1);
  153. #endif /* DEBUG */
  154. }
  155.  
  156. XQueryWindow(window,frame)
  157. Window window;
  158. WindowGeom *frame;
  159. {
  160.     XGetGeometry(dpy, window,
  161.          &(frame->root),
  162.          &(frame->x), &(frame->y),
  163.          &(frame->width), &(frame->height),
  164.          &(frame->border_width),
  165.          &(frame->depth));
  166. }
  167.  
  168. RectSet(W,x,y,w,h,pixel)
  169. Window W;
  170. int x,y;
  171. unsigned int w,h;
  172. unsigned long pixel;
  173. {
  174.     XSetForeground(dpy, rect_gc, pixel);
  175.     XFillRectangle(dpy, W, rect_gc, x, y, w, h);
  176. }
  177.  
  178. MoveArea(W,src_x,src_y,dst_x,dst_y,w,h)
  179. Window W;
  180. int src_x, src_y, dst_x, dst_y;
  181. unsigned int w, h;
  182. {
  183.     XCopyArea(dpy,W,W,gc,src_x,src_y,w,h,dst_x,dst_y);
  184. }
  185.  
  186. /** RepaintTitle - puts the program title in the title bar **/
  187.  
  188. RepaintTitle(method)
  189. int method;
  190. {
  191.     int Twidth,Theight;
  192.     int i,j, startColor,color2,tinyBoxSize;
  193.     int Tx, Ty;
  194.  
  195.     /*
  196.      * applications painting their own title is out of style,
  197.      * so don't just leave it there;
  198.      */
  199.  
  200.     tinyBoxSize = 5;
  201.     Twidth  = PuzzleWinInfo.width*3/4;
  202.     Tx      = (PuzzleWinInfo.width-Twidth)/2;
  203.     TextXStart = Tx;
  204.  
  205.     if (method == TITLE_TEXT) {
  206.     Twidth  = XTextWidth(TitleFontInfo,ProgName,strlen(ProgName));
  207.     Theight = TitleFontInfo->ascent + TitleFontInfo->descent;
  208.     Tx        = (PuzzleWinInfo.width-Twidth)/2;
  209.     Ty        = (TitleWinHeight-Theight)/2 + TitleFontInfo->ascent;
  210.  
  211.     XSetFont(dpy, gc, TitleFontInfo->fid);
  212.     XDrawString(dpy, TitleWindow, gc,Tx, Ty, ProgName,strlen(ProgName));
  213.     XFlush(dpy);
  214.     }
  215.     else if (method == TITLE_TILES) {
  216.     for (i=0,startColor=0; i<TitleWinHeight; i+=tinyBoxSize,startColor++)
  217.         for (j=0,color2=startColor; j<Twidth; j+=tinyBoxSize,color2++)
  218.         RectSet(TitleWindow,j+TextXStart,i,tinyBoxSize,tinyBoxSize,
  219.             color2%2);
  220.     }
  221.     else {
  222.     /** method == TITLE_ANIMATED **/
  223.  
  224.     unsigned char *colorVal;
  225.     int *xLoc, *yLoc, *permute;
  226.     int tilesHigh, tilesWide, numTiles, counter, swapWith, tmp;
  227.  
  228.     tilesHigh = (TitleWinHeight+tinyBoxSize-1)/tinyBoxSize;
  229.     tilesWide = (Twidth+tinyBoxSize-1)/tinyBoxSize;
  230.     numTiles = tilesHigh * tilesWide;
  231.  
  232.     colorVal = (unsigned char *) malloc(numTiles);
  233.     xLoc = (int *) malloc(numTiles * sizeof(int));
  234.     yLoc = (int *) malloc(numTiles * sizeof(int));
  235.     permute = (int *) malloc(numTiles * sizeof(int));
  236.  
  237.     for (i=0; i<numTiles; i++)
  238.         permute[i] = i;
  239.  
  240.     for (i=numTiles-1; i>1; i--) {
  241.         swapWith = rand()%i;
  242.         tmp = permute[swapWith];
  243.         permute[swapWith] = permute[i];
  244.         permute[i] = tmp;
  245.     }
  246.  
  247.     counter = 0;
  248.     for (i=0,startColor=0; i<TitleWinHeight; i+=tinyBoxSize,startColor++)
  249.         for (j=0,color2=startColor; j<Twidth; j+=tinyBoxSize,color2++) {
  250.         colorVal[counter] = color2%2;
  251.         xLoc[counter] = j+TextXStart;
  252.         yLoc[counter] = i;
  253.         counter++;
  254.         }
  255.  
  256.     for (i=0; i<numTiles; i++) {
  257.         j = permute[i];
  258.         RectSet(TitleWindow,xLoc[j],yLoc[j],tinyBoxSize,tinyBoxSize,
  259.             colorVal[j]);
  260.         XFlush(dpy);
  261.     }
  262.  
  263.     free(colorVal);
  264.     free(xLoc);
  265.     free(yLoc);
  266.     free(permute);
  267.     }
  268. }
  269.  
  270. /*
  271.  * RepaintBar - Repaint the bar between the title window and
  272.  *              the tile window;
  273.  */
  274. RepaintBar()
  275. {
  276.     XFillRectangle(dpy, PuzzleRoot, gc,
  277.            0, TitleWinHeight,
  278.            PuzzleWinInfo.width, BoundaryHeight);
  279. }
  280.  
  281. /**
  282.  ** RepaintTiles - draw the numbers in the tiles to match the
  283.  **                locations array;
  284.  **/
  285. RepaintTiles()
  286. {
  287. #ifdef USE_PICTURE
  288.    if (UsePicture)
  289.       RepaintPictureTiles();
  290.    else
  291. #endif /* USE_PICTURE */
  292.       RepaintNumberTiles();
  293. }
  294.  
  295. RepaintNumberTiles()
  296. {
  297.     int i,j,counter;
  298.     int width,height;
  299.     int x_offset,y_offset;
  300.     char str[30];
  301.     
  302.     /** cut the TileWindow into a grid of nxn pieces by inscribing
  303.      ** each rectangle with a black border;
  304.      ** I don't want to use subwindows for each tile so that I can
  305.      ** slide groups of tiles together as a single unit, rather than
  306.      ** being forced to move one tile at a time.
  307.      **/
  308.  
  309. #define line(x1,y1,x2,y2) XDrawLine(dpy,TileWindow,gc,(x1),(y1),(x2),(y2))
  310.  
  311. #define rect(x,y)    (line(ulx(x,y),uly(x,y),urx(x,y),ury(x,y)),    \
  312.              line(urx(x,y),ury(x,y),lrx(x,y),lry(x,y)),    \
  313.              line(lrx(x,y),lry(x,y),llx(x,y),lly(x,y)),    \
  314.              line(llx(x,y),lly(x,y),ulx(x,y),uly(x,y)))
  315.  
  316.     height = TileFontInfo->ascent + TileFontInfo->descent;
  317.     y_offset = (TileHeight - height)/2 + TileFontInfo->ascent;
  318.  
  319.     XSetFont(dpy, gc, TileFontInfo->fid);
  320.  
  321.     counter = 0;
  322.     for (i=0; i<PuzzleHeight; i++)
  323.     for (j=0; j<PuzzleWidth; j++) {
  324.         if (position[counter] == 0) {
  325.         RectSet(TileWindow,ulx(j,i),uly(j,i),
  326.             TileWidth,TileHeight,FgPixel);
  327.         }
  328.         else {
  329.         RectSet(TileWindow,ulx(j,i),uly(j,i),TileWidth,TileHeight,
  330.             BgPixel);
  331.         rect(j,i);
  332.         sprintf(str,"%d",position[counter]);
  333.         width = XTextWidth(TileFontInfo,str,strlen(str));
  334.         x_offset = (TileWidth - width)/2;
  335.         XDrawString(dpy, TileWindow, gc,
  336.                 ulx(j,i)+x_offset,uly(j,i)+y_offset,
  337.                 str,strlen(str));
  338.         }
  339.         counter++;
  340.     }    
  341. }
  342.  
  343. #ifdef USE_PICTURE
  344. RepaintPictureTiles()
  345. {
  346.     int i, j, counter;
  347.     int tmp, orig_x,orig_y;
  348.  
  349.     counter = 0;
  350.     for (i=0; i<PuzzleHeight; i++)
  351.     for (j=0; j<PuzzleWidth; j++) {
  352.         if (position[counter] == 0)
  353.         RectSet(TileWindow,ulx(j,i),uly(j,i),
  354.             TileWidth,TileHeight,FgPixel);
  355.         else {
  356.         tmp = position[counter] - 1;
  357.         orig_x = tmp % PuzzleWidth;
  358.         orig_y = tmp / PuzzleWidth;
  359.  
  360.         XCopyArea(dpy,PicturePixmap,TileWindow,gc,
  361.               ulx(orig_x,orig_y), uly(orig_x,orig_y),
  362.               TileWidth, TileHeight,
  363.               ulx(j,i), uly(j,i));
  364.         }
  365.         counter++;
  366.     }    
  367.     
  368. }
  369. #endif /* USE_PICTURE */
  370.  
  371. /**
  372.  ** Setup - Perform initial window creation, etc.
  373.  **/
  374.  
  375. Setup (geom,argc,argv)
  376. char *geom;
  377. int argc;
  378. char *argv[];
  379. {
  380.     int minwidth, minheight;
  381.     Pixmap PictureSetup();
  382.     Visual visual;
  383.     XGCValues xgcv;
  384.     XSetWindowAttributes xswa;
  385.     XSizeHints sizehints;
  386.  
  387.     /*******************************************/
  388.     /** let the puzzle code initialize itself **/
  389.     /*******************************************/
  390.     initialize();
  391.     OutputLogging = 1;
  392.  
  393.     FgPixel = BlackPixel(dpy,screen);
  394.     BgPixel = WhitePixel(dpy,screen);
  395.  
  396.     TitleWinHeight = TITLE_WINDOW_HEIGHT;
  397.     BoundaryHeight = BOUNDARY_HEIGHT;
  398.  
  399. #ifdef USE_PICTURE
  400.     /*****************************************************/
  401.     /** if we want to use a picture file, initialize it **/
  402.     /*****************************************************/
  403.     if (UsePicture) {
  404.     /**
  405.      ** This was fun to do back with X10 when you could create
  406.      ** a pixmap from the current display contents;  No more, I guess.
  407.      **/
  408. #ifdef UNDEFINED
  409.     if (UseDisplay) {
  410.         WindowGeom RootWinInfo;
  411.         int x,y;
  412.  
  413.         x = PUZZLE_BORDER_WIDTH;
  414.         y = TITLE_WINDOW_HEIGHT + BOUNDARY_HEIGHT + PUZZLE_BORDER_WIDTH;
  415.         XQueryWindow(RootWindow(dpy, screen),&RootWinInfo);
  416.         PictureWidth  = RootWinInfo.width  - x;
  417.         PictureHeight = RootWinInfo.height - y;
  418.         PicturePixmap = XPixmapSave(RootWindow(dpy,screen),
  419.                     x,y,PictureWidth,PictureHeight);
  420.     }
  421.     else
  422. #endif /* UNDEFINED */
  423.         PicturePixmap = PictureSetup(PictureFileName,&PictureWidth,
  424.                      &PictureHeight);
  425.     }
  426. #endif /* USE_PICTURE */
  427.  
  428. #ifdef USE_PICTURE
  429.     if (UsePicture) {
  430.     minwidth = PictureWidth;
  431.     minheight = PictureHeight + TITLE_WINDOW_HEIGHT + BOUNDARY_HEIGHT;
  432.     }
  433.     else {
  434. #endif /* USE_PICTURE */
  435.     minwidth = MIN_TILE_WIDTH * PuzzleWidth;
  436.     minheight = MIN_TILE_HEIGHT * PuzzleHeight + TITLE_WINDOW_HEIGHT +
  437.         BOUNDARY_HEIGHT;
  438. #ifdef USE_PICTURE 
  439.     }
  440. #endif /* USE_PICTURE */
  441.  
  442.     /*************************************/
  443.     /** configure the window size hints **/
  444.     /*************************************/
  445.  
  446.     {
  447.     int x, y, width, height;
  448.     int tileHeight, tileWidth;
  449.     int flags;
  450.  
  451.     sizehints.flags = PMinSize | PPosition | PSize | PResizeInc;
  452.     sizehints.min_width = minwidth;
  453.     sizehints.min_height = minheight;
  454.     sizehints.width = minwidth;
  455.     sizehints.height = minheight;
  456.     sizehints.x = 100;
  457.     sizehints.y = 300;
  458.     sizehints.width_inc = PuzzleWidth;
  459.     sizehints.height_inc = PuzzleHeight;
  460.  
  461. #ifdef USE_PICTURE    
  462.     if (UsePicture) {
  463.         sizehints.flags |= PMaxSize;
  464.         sizehints.max_width = sizehints.min_width;
  465.         sizehints.max_height = sizehints.min_height;
  466.     }
  467. #endif /* USE_PICTURE */
  468.  
  469.     if(strlen(geom)) {
  470.         flags = XParseGeometry(geom, &x, &y,
  471.                    (unsigned int *)&width,
  472.                    (unsigned int *)&height);
  473.         if(WidthValue & flags) {
  474.         sizehints.flags |= USSize;
  475.         if (width > sizehints.min_width)
  476.             sizehints.width = width;
  477.         }
  478.         if(HeightValue & flags) {
  479.         sizehints.flags |= USSize;
  480.         if (height > sizehints.min_height)
  481.             sizehints.height = height;
  482.         }
  483.         if(XValue & flags) {
  484.         if(XNegative & flags)
  485.             x = DisplayWidth(dpy, DefaultScreen(dpy)) + x 
  486.             - sizehints.width;
  487.         sizehints.flags |= USPosition;
  488.         sizehints.x = x;
  489.         }
  490.         if(YValue & flags) {
  491.         if(YNegative & flags)
  492.             y = DisplayHeight(dpy, DefaultScreen(dpy)) + y
  493.             -sizehints.height;
  494.         sizehints.flags |= USPosition;
  495.         sizehints.y = y;
  496.         }
  497.  
  498.         tileHeight = (sizehints.height-TitleWinHeight-BoundaryHeight)/PuzzleHeight;
  499.         sizehints.height = tileHeight*PuzzleHeight+TitleWinHeight+BoundaryHeight;
  500.  
  501.         tileWidth = sizehints.width/PuzzleWidth;
  502.         sizehints.width = tileWidth * PuzzleWidth;
  503.     }
  504.     }
  505.  
  506.     /*******************************************************************/
  507.     /** create the puzzle main window and set its standard properties **/
  508.     /*******************************************************************/
  509.  
  510.     xswa.event_mask = ExposureMask;
  511.     visual.visualid = CopyFromParent;
  512.  
  513.     PuzzleRoot = XCreateSimpleWindow(dpy, RootWindow(dpy,screen),
  514.                    sizehints.x, sizehints.y,
  515.                    sizehints.width, sizehints.height,
  516.                    PUZZLE_BORDER_WIDTH, FgPixel,FgPixel);
  517.  
  518.     XSetStandardProperties(dpy, PuzzleRoot,"puzzle","Puzzle",
  519.                None, argv, argc, &sizehints);
  520.  
  521.    if (CreateNewColormap)
  522.        XSetWindowColormap(dpy, PuzzleRoot, PuzzleColormap);
  523.  
  524.     xgcv.foreground = FgPixel;
  525.     xgcv.background = BgPixel;
  526.     xgcv.line_width = 1;
  527.     gc = XCreateGC(dpy, PuzzleRoot,
  528.            GCForeground|GCBackground|GCLineWidth,
  529.            &xgcv);
  530.  
  531.     /*********************************/
  532.     /** load the arrow-cross cursor **/
  533.     /*********************************/
  534.     
  535.     {
  536.     Pixmap ACPixmap, ACMask;
  537.     Cursor ACCursor;
  538.     XColor FGcolor, BGcolor;
  539.  
  540.     FGcolor.red = 0;    FGcolor.green = 0;    FGcolor.blue = 0;
  541.     BGcolor.red = 0xffff;    BGcolor.green = 0xffff;    BGcolor.blue = 0xffff;
  542.  
  543.     ACPixmap = XCreateBitmapFromData(dpy,RootWindow(dpy,screen),
  544.                      (char *) ac_bits,
  545.                      ac_width, ac_height);
  546.     ACMask = XCreateBitmapFromData(dpy,RootWindow(dpy,screen),
  547.                        (char *) ac_mask_bits,
  548.                        ac_mask_width, ac_mask_height);
  549.     ACCursor = XCreatePixmapCursor(dpy,ACPixmap,ACMask,
  550.                        &FGcolor,&BGcolor,
  551.                        ac_x_hot, ac_y_hot);
  552.     if (ACCursor == (Cursor)NULL)
  553.         error("Unable to store ArrowCrossCursor.");
  554.  
  555.     XDefineCursor(dpy,PuzzleRoot,ACCursor);
  556.     }
  557.  
  558.     /*****************************************/
  559.     /** allocate the fonts we will be using **/
  560.     /*****************************************/
  561.  
  562.     TitleFontInfo    = XLoadQueryFont(dpy,TitleFontName);
  563.     TileFontInfo     = XLoadQueryFont(dpy,TileFontName);
  564.  
  565.     if (TitleFontInfo    == NULL) error("Opening title font.\n");
  566.     if (TileFontInfo     == NULL) error("Opening tile font.\n");
  567.  
  568.     XSelectInput(dpy, PuzzleRoot, ExposureMask|VisibilityChangeMask);
  569.     XMapWindow(dpy,PuzzleRoot);
  570. }
  571.  
  572. static short old_height = -1;
  573. static short old_width = -1;
  574.  
  575. SizeChanged()
  576. {
  577.     XQueryWindow(PuzzleRoot,&PuzzleWinInfo);
  578.     
  579.     if (PuzzleWinInfo.width == old_width &&
  580.     PuzzleWinInfo.height == old_height)
  581.     return(0);
  582.     else
  583.     return(1);
  584. }
  585.  
  586. Reset()
  587. {
  588.     int Box_x,Box_y;
  589.     int TileBgPixel;
  590.     
  591.     /** TileWindow is that portion of PuzzleRoot that contains
  592.      ** the sliding pieces;
  593.      **/
  594.  
  595.     if (UsePicture)
  596.     TileBgPixel = BlackPixel(dpy,screen);
  597.     else
  598.     TileBgPixel = WhitePixel(dpy,screen);
  599.     
  600. #ifdef SERVER_BUG
  601.     /* seems I need to do this, or the next title window will be obscured
  602.      * by the old title window! This must be a server bug, right?
  603.      */
  604.     if (TitleWindow) XUnmapWindow(dpy,TitleWindow);
  605. /*    if (TitleWindow) XDestroyWindow(dpy,TitleWindow); */
  606. #endif /* SERVER_BUG */
  607.     XDestroySubwindows(dpy,PuzzleRoot);
  608.  
  609.     /** fix the dimensions of PuzzleRoot so the height and width
  610.      ** of the TileWindow will work out to be multiples of PuzzleSize;
  611.      **/
  612.  
  613.     /** If we're dealing with a picture, the tile region can be no larger
  614.      ** than the picture!
  615.      **/
  616.  
  617. #ifdef USE_PICTURE
  618.     if (UsePicture) {
  619.     int tmp;
  620.  
  621.     tmp = PuzzleWinInfo.height - TitleWinHeight - BoundaryHeight;
  622.     if (tmp > PictureHeight)
  623.         PuzzleWinInfo.height = PictureHeight+TitleWinHeight+BoundaryHeight;
  624.     if (PuzzleWinInfo.width > PictureWidth)
  625.         PuzzleWinInfo.width = PictureWidth;
  626.     }
  627. #endif /* USE_PICTURE */
  628.  
  629.     TileHeight=(PuzzleWinInfo.height-TitleWinHeight-BoundaryHeight)/PuzzleHeight;
  630.     /* PuzzleWinInfo.height = TileHeight*PuzzleHeight+TitleWinHeight+BoundaryHeight; */
  631.  
  632.     TileWidth = PuzzleWinInfo.width/PuzzleWidth;
  633.     /* PuzzleWinInfo.width = TileWidth * PuzzleWidth; */
  634.  
  635.     /** fixup the size of PuzzleRoot **/
  636.  
  637.     /* XResizeWindow(dpy,PuzzleRoot,PuzzleWinInfo.width,PuzzleWinInfo.height); */
  638.     old_width  = PuzzleWinInfo.width;
  639.     old_height = PuzzleWinInfo.height;
  640.  
  641.     TileWinHeight = PuzzleWinInfo.height - TitleWinHeight;
  642.  
  643.     TitleWindow = XCreateSimpleWindow(dpy, PuzzleRoot,
  644.             0,0,
  645.             PuzzleWinInfo.width, TitleWinHeight,
  646.             0,0,BgPixel);
  647.  
  648.     TileWindow  = XCreateSimpleWindow(dpy, PuzzleRoot,
  649.             0,TitleWinHeight+BoundaryHeight,
  650.             PuzzleWinInfo.width, TileWinHeight,
  651.             0,0,TileBgPixel);
  652.    
  653.     rect_gc = XCreateGC(dpy,TileWindow,0,0);
  654.     XCopyGC(dpy, gc, -1, rect_gc);
  655.  
  656.     XMapWindow(dpy,TitleWindow);
  657.     XMapWindow(dpy,TileWindow);
  658.     XSync(dpy,0);
  659.  
  660.     RepaintBar();
  661.     RepaintTitle(TITLE_TEXT);
  662.  
  663.     /** locate the two check boxes **/
  664.  
  665.     Box_x = TextXStart/2 - BoxWidth/2;
  666.     Box_y = TitleWinHeight/2 - BoxHeight/2;
  667.     
  668.     ScrambleWindow = XCreateSimpleWindow(dpy, TitleWindow,
  669.                  Box_x, Box_y,
  670.                  BoxWidth, BoxHeight,
  671.                  1,FgPixel,BgPixel);
  672.  
  673.     Box_x = PuzzleWinInfo.width - Box_x - BoxWidth;
  674.     
  675.     SolveWindow = XCreateSimpleWindow(dpy, TitleWindow,
  676.                  Box_x,Box_y,
  677.                  BoxWidth,BoxHeight,
  678.                  1,FgPixel,BgPixel);
  679.  
  680.     XMapWindow(dpy,ScrambleWindow);
  681.     XMapWindow(dpy,SolveWindow);
  682.     XSync(dpy,0);
  683.  
  684.     XSelectInput(dpy, TitleWindow,   ButtonPressMask|ExposureMask);
  685.     XSelectInput(dpy, TileWindow,    ButtonPressMask|ExposureMask|
  686.                      VisibilityChangeMask);
  687.     XSelectInput(dpy, ScrambleWindow,ButtonPressMask|ExposureMask);
  688.     XSelectInput(dpy, SolveWindow,   ButtonPressMask|ExposureMask);
  689.  
  690.     RepaintTiles();
  691.     RepaintTitle(TITLE_ANIMATED);
  692.     CalculateSpeed();
  693.     CalculateStepsize();
  694.     XSync(dpy,0);
  695. }
  696.  
  697. /*
  698.  * Sets the global variable MoveSteps based on speed
  699.  * specified on the command line;
  700.  */
  701.  
  702. #if defined(USG) && !defined(Cray)    /* tv_usec never changes */
  703. #define MIN_DELTA_T 100L
  704. #else                    /* don't divide by zero */
  705. #define MIN_DELTA_T 1L
  706. #endif
  707.  
  708. /** delta-t in miliseconds **/
  709. #define DeltaT(tv2,tv1)                \
  710.     ( ((tv2.tv_sec  - tv1.tv_sec )*1000L)    \
  711.      +((tv2.tv_usec - tv1.tv_usec)/1000L))
  712.  
  713. CalculateSpeed()
  714. {
  715.     struct timeval tv1, tv2;
  716.     struct timezone tz;
  717.     int i, x, y;
  718.     long timePerTile;
  719.     static int firstCall = 1;
  720.     long delta;
  721.  
  722.     if (!firstCall)
  723.     return;
  724.     firstCall = 0;
  725.  
  726.     x = space_x * TileWidth;
  727.     y = space_y * TileHeight;
  728.     timePerTile = (long)(1000/TilesPerSecond);
  729.  
  730.     XSync(dpy,0);
  731.     gettimeofday(&tv1, &tz);
  732.     tv2 = tv1;
  733.  
  734.     MoveSteps = 0;
  735.     delta = 0L;
  736.     while (delta < timePerTile) {
  737.     MoveArea(TileWindow,x,y,x+1,y,TileWidth,TileHeight);
  738.     RectSet(TileWindow,x,y,1,TileHeight,FgPixel);
  739.     XSync(dpy,0);
  740.     gettimeofday(&tv2, &tz);
  741.     delta = DeltaT(tv2,tv1);
  742.     delta = max(MIN_DELTA_T, delta);
  743.     if (delta >= 0) MoveSteps++;    /* crock for broken systems */
  744.     }
  745.  
  746.     /*
  747.      * now, see how long this takes without all the extra b.s.
  748.      * and compensate;       
  749.      */
  750.  
  751.     XSync(dpy,0);
  752.     gettimeofday(&tv1, &tz);
  753.     for (i=0; i<MoveSteps; i++) {
  754.     MoveArea(TileWindow,x,y,x+1,y,TileWidth,TileHeight);
  755.     RectSet(TileWindow,x,y,1,TileHeight,FgPixel);
  756.     }
  757.     XFlush(dpy);
  758.     gettimeofday(&tv2, &tz);
  759.     delta = DeltaT(tv2, tv1);
  760.     delta = max(MIN_DELTA_T, delta);
  761.     MoveSteps = (((long)MoveSteps) * timePerTile)/(delta ? delta : 1L);
  762.     if (MoveSteps <= 0)
  763.     MoveSteps = 1;
  764. }
  765.  
  766. CalculateStepsize()
  767. {
  768.     int i, rem;
  769.     int error,sum;
  770.  
  771.     for (i=0; i<MoveSteps; i++)
  772.     VertStepSize[i] = TileHeight/MoveSteps;
  773.          
  774.     rem = TileHeight % MoveSteps;
  775.     error = - MoveSteps/2;
  776.  
  777.     if (rem > 0)
  778.     for (i=0; i<MoveSteps; i++) {
  779.         if (error >= 0) {
  780.         VertStepSize[i]++;
  781.         error -= MoveSteps;
  782.         }
  783.         error += rem;
  784.     }   
  785.     
  786.     for (i=0; i<MoveSteps; i++)
  787.     HoriStepSize[i] = TileWidth/MoveSteps;
  788.          
  789.     rem = TileWidth % MoveSteps;
  790.     error = - MoveSteps/2;
  791.  
  792.     if (rem > 0)
  793.     for (i=0; i<MoveSteps; i++) {
  794.         if (error >= 0) {
  795.         HoriStepSize[i]++;
  796.         error -= MoveSteps;
  797.         }
  798.         error += rem;
  799.     }   
  800.  
  801.     /** This code is a little screwed up and I don't want to fix it
  802.      ** right now, so just do a little hack to make sure the total
  803.      ** distance comes out right;
  804.      **/
  805.  
  806.     sum = 0;
  807.     for (i=0; i<MoveSteps; i++)
  808.     sum += HoriStepSize[i];
  809.     HoriStepSize[0] += TileWidth - sum;
  810.  
  811.     sum = 0;
  812.     for (i=0; i<MoveSteps; i++)
  813.     sum += VertStepSize[i];
  814.     VertStepSize[0] += TileHeight - sum;
  815. }
  816.  
  817. SlidePieces(event)
  818. XButtonEvent *event;
  819. {
  820.     int x,y;
  821.  
  822.     x = (*event).x / TileWidth;
  823.     y = (*event).y / TileHeight;
  824.     if (x == space_x || y == space_y)
  825.     move_space_to(indx(x,y));   
  826.     flushLogging();
  827. }
  828.  
  829. ProcessVisibility(event)
  830. XVisibilityEvent *event;
  831. {
  832.     if (event->state != VisibilityUnobscured) AbortSolving();
  833. }
  834.  
  835. ProcessExpose(event)
  836. XExposeEvent *event;
  837. {
  838.     int loop  = 1;
  839.     int reset = 0,
  840.         title = 0,
  841.         tiles = 0,
  842.         bar   = 0;
  843.  
  844.     loop = 1;
  845.     while (loop) {
  846.     if (event->count == 0) {
  847.         if (event->window == TitleWindow)
  848.         title++;
  849.         else if (event->window == TileWindow)
  850.         tiles++;
  851.         else if (event->window == PuzzleRoot)
  852.         bar++;
  853.     }
  854.     loop = XCheckMaskEvent(dpy, ExposureMask, (XEvent *)event);
  855.     }
  856.  
  857.     if (SizeChanged())
  858.     reset++;
  859.  
  860.     if (reset)
  861.     Reset();
  862.     else {
  863.     if (title) RepaintTitle(TITLE_TILES);
  864.     if (tiles) RepaintTiles();
  865.     if (bar)   RepaintBar();
  866.     }
  867. }
  868.  
  869. ProcessButton(event)
  870. XButtonEvent *event;
  871. {
  872.     Window w;
  873.  
  874.     w = event->window;
  875.     if (w == TileWindow) {
  876.     if (SolvingStatus())
  877.         AbortSolving();
  878.     else
  879.         SlidePieces(event);
  880.     }
  881.     else if (w == ScrambleWindow) {
  882.     AbortSolving();
  883.     Scramble();
  884.     RepaintTiles();
  885.     }
  886.     else if (w == SolveWindow)
  887.     Solve();
  888.     else if ((w == TitleWindow) && (*event).button == Button2)
  889.     exit(0);
  890. }
  891.  
  892. ProcessInput()
  893. {
  894.     XEvent event;
  895.  
  896.     while(1) {
  897.     GetNextEvent(&event);  
  898.     ProcessEvent(&event);
  899.     }
  900. }
  901.  
  902. ProcessEvents()
  903. {
  904.     XEvent event;
  905.  
  906.     while(XPending(dpy)) {
  907.     GetNextEvent(&event);  
  908.     ProcessEvent(&event);
  909.     }
  910. }
  911.  
  912. GetNextEvent(event)
  913. XEvent *event;
  914. {
  915.     if (!XCheckMaskEvent(dpy,VisibilityChangeMask,event) &&
  916.     !XCheckMaskEvent(dpy,ExposureMask,event))
  917.     XNextEvent(dpy,event);  
  918. }
  919.  
  920. ProcessEvent(event)
  921. XEvent *event;
  922. {
  923.     switch(event->type) {
  924.       case ButtonPress:
  925.     ProcessButton(&event->xbutton);
  926.     break;
  927.       case Expose:
  928.     ProcessExpose(&event->xexpose);
  929.     break;
  930.       case VisibilityNotify:
  931.     ProcessVisibility(&event->xvisibility);
  932.     break;
  933.       default:
  934.     break;
  935.    }
  936. }
  937.  
  938. main(argc,argv)
  939. int argc;
  940. char *argv[];
  941. {
  942.    int i;
  943.    char *ServerName, *Geometry;
  944.    char *puzzle_size = NULL;
  945.    char *option;
  946.  
  947.    ProgName = argv[0];
  948.  
  949.    ServerName = "";
  950.    Geometry   = "";  
  951.    TilesPerSecond = -1;
  952.  
  953.    /********************************/
  954.    /** parse command line options **/
  955.    /********************************/
  956.  
  957.    for (i=1; i<argc; i++) {
  958.       char *arg = argv[i];
  959.  
  960.       if (arg[0] == '-') {
  961.     switch (arg[1]) {
  962.         case 'd':                /* -display host:dpy */
  963.         if (++i >= argc) usage ();
  964.         ServerName = argv[i];
  965.         continue;
  966.         case 'g':                /* -geometry geom */
  967.         if (++i >= argc) usage ();
  968.         Geometry = argv[i];
  969.         continue;
  970.         case 's':                /* -size WxH or -speed n */
  971.         if (arg[2] == 'i') {
  972.             if (++i >= argc) usage ();
  973.             puzzle_size = argv[i];
  974.             continue;
  975.         } else if (arg[2] == 'p') {
  976.             if (++i >= argc) usage ();
  977.             TilesPerSecond = atoi (argv[i]);
  978.             continue;
  979.         } else 
  980.             usage ();
  981.         break;
  982.         case 'p':                /* -picture filename */
  983.         if (++i >= argc) usage ();
  984.         UsePicture++;
  985.         PictureFileName = argv[i];
  986.         continue;
  987.         case 'c':                /* -colormap */
  988.         CreateNewColormap++;
  989.         continue;
  990.         default:
  991.         usage ();
  992.     }                    /* end switch */
  993.       }    else
  994.     usage ();
  995.    }                        /* end for */
  996.  
  997.    SetupDisplay (ServerName);
  998.  
  999.    if (!Geometry) {
  1000.     Geometry = XGetDefault (dpy, ProgName, "Geometry");
  1001.    }
  1002.  
  1003.    if (!puzzle_size) {
  1004.     option = XGetDefault (dpy, ProgName, "Size");
  1005.     puzzle_size = option ? option : defaultPuzzleSize;
  1006.    }
  1007.  
  1008.    if (TilesPerSecond <= 0) {
  1009.     option = XGetDefault (dpy, ProgName, "Speed");
  1010.     TilesPerSecond = option ? atoi (option) : DEFAULT_SPEED;
  1011.    }
  1012.  
  1013.    if (!UsePicture) {
  1014.     option = XGetDefault (dpy, ProgName, "Picture");
  1015.     if (option) {
  1016.         UsePicture++;
  1017.         PictureFileName = option;
  1018.     }
  1019.    }
  1020.  
  1021.    if (!CreateNewColormap) {
  1022.     option = XGetDefault (dpy, ProgName, "Colormap");
  1023.     if (option) {
  1024.         CreateNewColormap++;
  1025.     }
  1026.    }
  1027.  
  1028.    sscanf (puzzle_size, "%dx%d", &PuzzleWidth, &PuzzleHeight);
  1029.    if (PuzzleWidth < 4 || PuzzleHeight < 4) {
  1030.     fprintf (stderr, "%s:  Puzzle size must be at least 4x4\n",
  1031.          ProgName);
  1032.     exit (1);
  1033.    }
  1034.    PuzzleSize = min((PuzzleWidth/2)*2,(PuzzleHeight/2)*2);
  1035.  
  1036.    Setup (Geometry,argc,argv);
  1037.    ProcessInput();
  1038.    exit (0);
  1039. }
  1040.  
  1041. static char *help_message[] = {
  1042. "where options include:",
  1043. "    -display host:dpy                X server to use",
  1044. "    -geometry geom                   geometry of puzzle window",
  1045. "    -size WxH                        number of squares in puzzle",
  1046. "    -speed number                    tiles to move per second",
  1047. "    -picture filename                image to use for tiles",
  1048. "    -colormap                        create a new colormap",
  1049. NULL};
  1050.  
  1051.  
  1052. usage()
  1053. {
  1054.     char **cpp;
  1055.  
  1056.     fprintf (stderr, "usage:  %s [-options ...]\n\n", ProgName);
  1057.     for (cpp = help_message; *cpp; cpp++) {
  1058.     fprintf (stderr, "%s\n", *cpp);
  1059.     }
  1060.     fprintf (stderr, "\n");
  1061.     exit (1);
  1062. }
  1063.  
  1064. error(str)
  1065. char *str;
  1066. {
  1067.    fprintf(stderr,"Error %s\n",str);
  1068.    exit(1);
  1069. }
  1070.  
  1071. /**
  1072.  ** Output Routines -
  1073.  **/
  1074.  
  1075. resetLogging()
  1076. { }
  1077. flushLogging()
  1078. { }
  1079. saveLoggingState()
  1080. { }
  1081. LogMoveSpace(first_x,first_y,last_x,last_y,dir)
  1082. int first_x,first_y,last_x,last_y,dir;
  1083. {
  1084.     displayLogMoveSpace(first_x,first_y,last_x,last_y,dir);
  1085. }
  1086.  
  1087.  
  1088. #ifdef UNDEFINED
  1089. /** this stuff really isn't worth it; **/
  1090.  
  1091. static int prevDir = -1;
  1092. static int prevFirstX, prevFirstY, prevLastX, prevLastY; 
  1093.  
  1094. resetLogging()
  1095. {
  1096.     prevDir = -1;
  1097. }
  1098.  
  1099. flushLogging()
  1100. {
  1101.     if (prevDir != -1)
  1102.     displayLogMoveSpace(prevFirstX,prevFirstY,prevLastX,prevLastY,prevDir);
  1103.     prevDir = -1;
  1104. }
  1105.  
  1106. saveLoggingState(fx,fy,lx,ly,dir)
  1107. int fx,fy,lx,ly,dir;
  1108. {
  1109.     prevDir = dir;
  1110.     prevFirstX = fx;
  1111.     prevFirstY = fy;
  1112.     prevLastX = lx;
  1113.     prevLastY = ly;
  1114. }
  1115.  
  1116. LogMoveSpace(first_x,first_y,last_x,last_y,dir)
  1117. int first_x,first_y,last_x,last_y,dir;
  1118. {
  1119.     if (prevDir == -1)
  1120.     /** we don't already have something to move **/
  1121.     saveLoggingState(first_x,first_y,last_x,last_y,dir);
  1122.     else if (prevDir == dir) {
  1123.     /** we're going in the same direction **/
  1124.     prevLastX = last_x;
  1125.     prevLastY = last_y;
  1126.     }
  1127.     else {
  1128.     flushLogging();
  1129.     saveLoggingState(first_x,first_y,last_x,last_y,dir);
  1130.     }
  1131. }
  1132. #endif /* UNDEFINED */
  1133.  
  1134. displayLogMoveSpace(first_x,first_y,last_x,last_y,dir)
  1135. int first_x,first_y,last_x,last_y,dir;
  1136. {
  1137.    int min_x,min_y,max_x,max_y;
  1138.    int x,y,w,h,dx,dy,x2,y2;
  1139.    int i, clear_x, clear_y;
  1140.  
  1141.  
  1142.    max_x = max(first_x,last_x);
  1143.    min_x = min(first_x,last_x);
  1144.    max_y = max(first_y,last_y);
  1145.    min_y = min(first_y,last_y);
  1146.  
  1147.    x = ulx(min_x,0);
  1148.    y = uly(0,min_y);   
  1149.    w = (max_x - min_x + 1)*TileWidth;
  1150.    h = (max_y - min_y + 1)*TileHeight;
  1151.  
  1152.    dx = x;
  1153.    dy = y;
  1154.  
  1155.    x2 = x;
  1156.    y2 = y;
  1157.  
  1158.    switch(dir) {
  1159.    case UP:    clear_x = llx(max_x,0);
  1160.                 clear_y = lly(0,max_y) + 1;
  1161.  
  1162.         for (i=0; i<MoveSteps; i++) {
  1163.                    dy = VertStepSize[i];
  1164.                    y2 = y - dy;
  1165.                    clear_y -= dy;
  1166.                    
  1167.                    MoveArea(TileWindow,x,y,x2,y2,w,h);
  1168.                    RectSet(TileWindow,clear_x,clear_y,
  1169.                    TileWidth,dy,FgPixel);
  1170.                    y -= dy;
  1171.                 }
  1172.         break;
  1173.    case DOWN:    clear_x = llx(max_x,0);
  1174.         clear_y = uly(0,min_y);
  1175.  
  1176.         for (i=0; i<MoveSteps; i++) {
  1177.            dy = VertStepSize[i];
  1178.            y2 = y + dy;
  1179.  
  1180.            MoveArea(TileWindow,x,y,x2,y2,w,h);
  1181.                    RectSet(TileWindow,clear_x,clear_y,
  1182.                    TileWidth,dy,FgPixel);
  1183.            y += dy;
  1184.            clear_y += dy;
  1185.         }
  1186.         break;
  1187.    case LEFT:    clear_x = urx(max_x,0) + 1;
  1188.         clear_y = ury(0,max_y);
  1189.  
  1190.         for (i=0; i<MoveSteps; i++) {
  1191.            dx = HoriStepSize[i];
  1192.            x2 = x - dx;
  1193.            clear_x -= dx;
  1194.  
  1195.            MoveArea(TileWindow,x,y,x2,y2,w,h);
  1196.                    RectSet(TileWindow,clear_x,clear_y,
  1197.                    dx,TileHeight,FgPixel);
  1198.            x -= dx;
  1199.         }
  1200.         break;
  1201.    case RIGHT:    clear_x = ulx(min_x,0);
  1202.         clear_y = uly(0,max_y);
  1203.         
  1204.                 for (i=0; i<MoveSteps; i++) {
  1205.            dx = HoriStepSize[i];
  1206.            x2 = x + dx;
  1207.  
  1208.            MoveArea(TileWindow,x,y,x2,y2,w,h);
  1209.                    RectSet(TileWindow,clear_x,clear_y,
  1210.                    dx,TileHeight,FgPixel);
  1211.            x += dx;
  1212.            clear_x += dx;
  1213.         }
  1214.         break;
  1215.    }
  1216.  
  1217.    XFlush(dpy);
  1218. }
  1219.